using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Text.RegularExpressions;
namespace DAL
{
    public class YCodes : DAL.DataManager<YCode>
    {
        private static YCodes Instance = new YCodes();
        static YCodes() { }

        public override string TableName
        {
            get { return @"fstdt_ycodes"; }
        }

        protected override void CopyToItem(DataRow Row, YCode Item)
        {
            Item.Position = Convert.ToInt64(Row["Position"]);

            Item.Code = Convert.ToString(Row["Code"]);

            Item.TotalArguments = Convert.ToInt32(Row["Arguments"]);

            Item.Template = Convert.ToString(Row["Template"]);

            Item.SupportNestedTags = Convert.ToBoolean(Row["SupportNestedTags"]);
        }

        protected override void CopyFromItem(DataManagerQuery query, YCode Item)
        {
            query.AddParam("Position", Item.Position);

            query.AddParam("Code", Item.Code);

            query.AddParam("Arguments", Item.TotalArguments);

            query.AddParam("Template", Item.Template);

            query.AddParam("SupportNestedTags", Item.SupportNestedTags);
        }

        public static YCode Item(Int64 id)
        {
            return Instance.GetItem(id);
        }

        public static List<YCode> All()
        {
            DataManagerQuery query = new DataManagerQuery();
            query.AddSort("Position", Sort.Asc);
            return Instance.GetItems(query);
        }

        public static void Add(YCode Item)
        {
            Item.Code = Item.Code.ToUpper();
            Instance.Insert(Item);
            Item.Position = Item.ID;
            Save(Item);
            PrecompiledCodes = null;
        }

        public static void Save(YCode Item)
        {
            Instance.Update(Item);
        }

        public static void Remove(YCode Item)
        {
            foreach (YCodeTransform transform in Item.Transforms)
            {
                YCodeTransforms.Remove(transform);
            }
            Instance.Delete(Item);
            PrecompiledCodes = null;
            
        }

        public static void Move(YCode Item, Direction dir)
        {
            //Helpers.PositionHelper<YCode> position = new Helpers.PositionHelper<YCode>();
            //position.MoveItem(Instance, Item, dir);
            PrecompiledCodes = null;
        }

        private static List<YCode> PrecompiledCodes;
        public static string Format(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return "";
            }
            else
            {
                input = System.Web.HttpContext.Current.Server.HtmlEncode(input);
                input = input.Replace("\n", "<br />");
                input = input.Replace("{", "&#123;");
                input = input.Replace("}", "&#125;");

                if (PrecompiledCodes == null)
                {
                    PrecompiledCodes = All();
                }
                foreach (YCode myCode in PrecompiledCodes)
                {
                    input = myCode.Format(input);
                }

                return input;
            }
        }
    }

    public class YCode : DAL.DataManagerRow, IRowPosition
    {
        private static Regex GetRegex(YCode Item)
        {
            return new Regex(GetRegexPattern(Item),
                RegexOptions.Singleline |
                RegexOptions.IgnoreCase |
                RegexOptions.IgnorePatternWhitespace |
                RegexOptions.Compiled);
        }

        private static Regex UrlRegex =new Regex(@"\b([\d\w\.\/\+\-\?\:]*)((ht|f)tp(s|)\:\/\/|[\d\d\d|\d\d]\.[\d\d\d|\d\d]\.|www\.|\.tv|\.ac|\.com|\.edu|\.gov|\.int|\.mil|\.net|\.org|\.biz|\.info|\.name|\.pro|\.museum|\.co)([\d\w\.\/\%\+\-\=\&amp;\?\:\\\&quot;\'\,\|\~\;]*)\b", RegexOptions.Compiled);

        private static string GetRegexPattern(YCode Item)
        {
            if (Item.SupportNestedTags)
            {
                // This is an example of a balanced regex:
                //\[quote\]
                //  (?>
                //      \[quote\]  (?<code>)
                //    | 
                //      \[/quote\] (?<-code>)
                //    |
                //      (?! \[quote\] | \[/quote\] ) .
                //  )+
                //\[/quote\]
                //(?(code)(?!))

                return string.Format
                    (
                    @"\[{0}{1}\]
                        (?<text>
                            (?>
                              \[{0}.*?\]  (?<code>)
                              |
                              \[/{0}\] (?<-code>)
                              |
                              (?! \[{0}.*?\] | \[/{0}\] ) .
                            )+
                        )
                    \[/{0}\]
                    (?(code)(?!))",
                    Item.Code,
                    GetRegexArgumentsPattern(Item.TotalArguments)
                    );
            }
            else
            {
                // Generates a regex that looks like this:
                //      (without arguments)
                //      \[b\](?<text>.*?)\[/b\]
                //
                //      (with arguments)
                //      \[b=(?<args>[^]]+)\](?<text>.*?)\[/b\]
                return String.Format(@"\[{0}{1}\](?<text>.*?)\[/{0}\]",
                    Item.Code,
                    GetRegexArgumentsPattern(Item.TotalArguments));
            }
        }

        private static string GetRegexArgumentsPattern(int numberOfArguments)
        {
            if (numberOfArguments > 0)
            {
                // matches any non-bracket character
                return @"=(?<args>[^]]+)";
            }
            else
            {
                return "";
            }
        }

        private Int64 _Position;
        public Int64 Position
        {
            get
            {
                return _Position;
            }
            set
            {
                _Position = value;
            }
        }


        private string _Code;
        public string Code
        {
            get
            {
                return _Code;
            }
            set
            {
                _Code = value;
            }
        }

        private string _Template;
        public string Template
        {
            get
            {
                return _Template;
            }
            set
            {
                _Template = value;
            }
        }

        private int _TotalArguments;
        public int TotalArguments
        {
            get
            {
                return _TotalArguments;
            }
            set
            {
                _TotalArguments = value;
            }
        }

        private bool _SupportNestedTags;
        public bool SupportNestedTags
        {
            get
            {
                return _SupportNestedTags;
            }
            set
            {
                _SupportNestedTags = value;
            }
        }

        private Regex _formatter;
        protected Regex Formatter
        {
            get
            {
                if (_formatter == null)
                {
                    _formatter = GetRegex(this);
                }
                return _formatter;
            }
            set
            {
                _formatter = value;
            }
        }

        private List<YCodeTransform> _transforms;
        public List<YCodeTransform> Transforms
        {
            get
            {
                if (_transforms == null)
                {
                    _transforms = DAL.YCodeTransforms.GetByYCode(this);
                }
                return _transforms;
            }
            set
            {
                _transforms = value;
            }
        }


        private string FormatWithTemplate(string input)
        {
            MatchCollection matches = Formatter.Matches(input);
            while (matches.Count > 0)
            {
                foreach (Match m in matches)
                {
                    string wholeMatch = m.Value;
                    string formattedMatch = Template;
                    string text = m.Groups["text"].Value;
                    formattedMatch = formattedMatch.Replace("{text}", text);
                    formattedMatch = formattedMatch.Replace("{text:url}", GetUrlString(text));

                    if (TotalArguments > 0)
                    {
                        string args = m.Groups["args"].Value;
                        string[] argArray = args.Split(';');
                        for (int i = 0; i <= TotalArguments - 1; i++)
                        {
                            if (i > argArray.Length - 1)
                            {
                                break;
                            }

                            string arg = argArray[i].Trim();
                            formattedMatch = formattedMatch.Replace(string.Format("{{arg{0}}}", i + 1), arg);
                            formattedMatch = formattedMatch.Replace(string.Format("{{arg{0}:url}}", i + 1), GetUrlString(arg));
                        }
                    }

                    foreach (YCodeTransform transform in Transforms)
                    {
                        formattedMatch = transform.Formatter.Replace(formattedMatch, transform.Replacement);
                    }

                    input = input.Replace(wholeMatch, formattedMatch);
                }

                matches = Formatter.Matches(input);
            }

            return input;
        }

        private string GetUrlString(string input)
        {
            input = System.Web.HttpContext.Current.Server.HtmlDecode(input);
            if (Uri.IsWellFormedUriString(input, UriKind.RelativeOrAbsolute) && UrlRegex.IsMatch(input))
            {
                return input;
            }
            else
            {
                return "#"; //harmless anchor tag
            }
        }

        public string Format(string input)
        {
            input = FormatWithTemplate(input);
            return input;
        }
    }
}